﻿using System;
using System.Collections.Generic;
using System.Linq;
using BL.Model.Request;
using Common;
using DAL.Model;

namespace BL
{
    public class StockInController
    {
        private readonly MasterSession _session;
        private readonly InventoryController _inventoryController;
        private readonly DocumentSeqNoHelper _documentSeqNoHelper;
        private readonly DocumentSeqNoHelper _productCodeSeqNoHelper;

        public StockInController(MasterSession session)
        {
            _session = session;
            _inventoryController = new InventoryController(_session);
            _documentSeqNoHelper = new DocumentSeqNoHelper("StockIn",_session);
            _productCodeSeqNoHelper = new DocumentSeqNoHelper("ProductCode", _session);
        }

        /// <summary>
        /// Creates a <see cref="StockIn"/> object
        /// </summary>
        /// <returns><see cref="StockIn"/> object</returns>
        public StockIn Create()
        {
            var stockIn = new StockIn
            {
                DocumentNo = _documentSeqNoHelper.GetNextDocSeqNo(),
                StockedDate = DateTime.Now
            };
            _session.StockIns.Add(stockIn);

            return stockIn;
        }

        /// <summary>
        /// Creates a details of <see cref="StockIn"/>
        /// </summary>
        /// <param name="stockIn">Parent <see cref="StockIn"/> object</param>
        /// <returns>A <see cref="StockInDetail"/> object</returns>
        public StockInDetail CreateDetail(StockIn stockIn)
        {
            var stockInDetail = new StockInDetail
                                    {
                                        StockIn = stockIn
                                    };
            _session.StockInDetails.Add(stockInDetail);

            return stockInDetail;
        }

        /// <summary>
        /// Gets a <see cref="StockIn"/> object by its identity.
        /// </summary>
        /// <param name="stockInId"></param>
        /// <returns></returns>
        public StockIn GetById(int stockInId)
        {
            var stockIn = _session.StockIns
                .FirstOrDefault(t => t.Id == stockInId);

            return stockIn;
        }
        
        /// <summary>
        /// Prepares data on saving including inventory data.
        /// </summary>
        /// <param name="stockIn">Input data</param>
        public void PrepareSavingData (StockIn stockIn)
        {
            if(stockIn == null)
            {
                return;
            }

            // check for change contact name of customer
            stockIn.Customer.ContactName = stockIn.ContactName;

            // check for warehouse
            if(stockIn.Warehouse == null)
            {
                var inWarehouse = _session.Warehouses
                    .FirstOrDefault(t => t.Id == Constant.StockInWarehouseId);
                stockIn.Warehouse = inWarehouse;
            }
            // merge inventory data
            var inventories = stockIn.Inventories.ToList();
            decimal totalQuantity = 0;
            foreach(var stockInDetail in stockIn.StockInDetails)
            {
                // get detail inventory for each stockInDetail
                var inventory = inventories
                                    .FirstOrDefault(t => t.ProductId == stockInDetail.ProductId) ??
                                _inventoryController.Create();
                // update detail
                inventory.StockIn = stockIn;
                inventory.Customer = stockIn.Customer;
                inventory.Warehouse = stockIn.Warehouse;
                inventory.IssuedDate = stockIn.StockedDate;
                inventory.Product = stockInDetail.Product;
                inventory.StockInQty = stockInDetail.Quantity;
                inventory.UnitOfMeasure = stockInDetail.UnitOfMeasure;
                inventory.PackageType = stockInDetail.PackageType;
                inventory.PackageQuantity = stockInDetail.PackageQuantity;
                inventory.PackageUom = stockInDetail.PackageUom;
                inventory.Note = stockInDetail.Note;
                inventory.Note2 = stockInDetail.Note2;
                inventory.PackageUom = stockInDetail.PackageUom;
                totalQuantity += stockInDetail.Quantity;

                // update related Transfer and StockOut
                foreach (var transferDetail in stockInDetail.TransferDetails)
                {
                    transferDetail.StockInQuantity = stockInDetail.Quantity;
                    foreach (var stockOutDetail in transferDetail.StockOutDetails)
                    {
                        stockOutDetail.StockInQuantity = stockInDetail.Quantity;
                    }
                }
            }
            stockIn.TotalQuantity = totalQuantity;

            // find inventory detail not in stockin detail
            // (cause stockin detail has been deleted)
            // just delete inventory
            // based on productId
            var stockInProductIds = stockIn.StockInDetails
                .Select(t => t.ProductId).ToArray();

            var trailInventories = stockIn.Inventories
                .Where(t => !stockInProductIds.Contains(t.ProductId))
                .ToArray();
            if (trailInventories.Any())
            {
                _session.Inventories.RemoveRange(trailInventories);
            }

            // get last productCode sequential
            var productCodes = stockIn.StockInDetails
                .Select(t => t.Product.Code)
                .ToList();
            int pcNewSeq = 0;
            foreach(var productCode in productCodes)
            {
                int iProductCode;
                if(!int.TryParse(productCode, out iProductCode))
                {
                    var pcSplited = productCode.Split('/');

                    if(!pcSplited.Any())
                    {
                        continue;
                    }
                    int.TryParse(pcSplited[0], out iProductCode);
                }

                if (iProductCode > pcNewSeq)
                {
                    pcNewSeq = iProductCode;
                }
            }
            if (pcNewSeq > 0)
            {
                _productCodeSeqNoHelper.UpdateDocSeqNo(pcNewSeq.ToString());
            }

            _documentSeqNoHelper.UpdateDocSeqNo(stockIn.DocumentNo);
        }

        /// <summary>
        /// Search product category by keyword with paging and ordering
        /// </summary>
        /// <param name="request"></param>
        /// <returns>List of product category</returns>
        /// <history>
        /// 11/30/2014 phat.vo: Create new
        /// </history>
        public List<StockIn> Search(SearchingStockInRequest request)
        {
            var isAllDocumentNo = string.IsNullOrWhiteSpace(request.DocumentNo);
            var documentNo = isAllDocumentNo ? string.Empty : request.DocumentNo.Trim().ToLower();

            var isAllProductCode = string.IsNullOrWhiteSpace(request.ProductCode);
            var productCode = isAllProductCode ? string.Empty : request.ProductCode.Trim().ToLower();

            var isAllProductCategory = request.ProductCategoryIds == null ||
                                       request.ProductCategoryIds.Contains(Constant.AllValue);
            var productCategoryIds = isAllProductCategory ? new List<int>() : request.ProductCategoryIds;

            var isAllCustomer = request.CustomerIds == null || request.CustomerIds.Contains(Constant.AllValue);
            var customerIds = isAllCustomer ? new List<int>() : request.CustomerIds;

            var isAllMerchant = request.MerchantIds == null || request.MerchantIds.Contains(Constant.AllValue);
            var merchantIds = isAllMerchant ? new List<int>() : request.MerchantIds;

            var query =
                (from stockIn in _session.StockIns
                 where (isAllDocumentNo || stockIn.DocumentNo.ToLower() == documentNo) &&
                       (isAllProductCode || stockIn.StockInDetails
                                                .Any(detail => detail.Product.Code.ToLower() == productCode)) &&
                       (isAllProductCategory ||
                        stockIn.StockInDetails
                            .Any(detail => detail.Product.ProductCategoryId.HasValue &&
                                           productCategoryIds.Contains(detail.Product.ProductCategoryId.Value))) &&
                       (!request.FromTime.HasValue || request.FromTime <= stockIn.StockedDate) &&
                       (!request.ToTime.HasValue || request.ToTime >= stockIn.StockedDate) &&
                       (isAllMerchant || (stockIn.MerchantId.HasValue &&
                                          merchantIds.Contains(stockIn.MerchantId.Value))) &&
                       (isAllCustomer || customerIds.Contains(stockIn.CustomerId))
                 select stockIn)
                    .OrderByDescending(t => t.StockedDate);

            return query.ToList();
        }
    }
}
